#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "check.h"
#include "TableSym.h"



int checkAllFct(FCTSYM symTRec, FCTSYM root)
{	
	printf(";symT: %s \n", symTRec->name);
	if (checkFct (symTRec->locationOnTree, symTRec, root) == 0) // vérifie la fonction courante
		return 0;

	else if(symTRec->childFct==NULL) // vérifier si il y a des fonctions imbriquées
	{
		if(symTRec->next!=NULL) // fonction au même niveau
		{
			return checkAllFct(symTRec->next, root);
		}
	}
	else
	{
		return checkAllFct(symTRec->childFct, root);
	}

	if(symTRec==NULL) 
		printf(";FIN de symtable\n");	
	
	return 1;
}

int  getNbArgCallFct(TREE tree) 
{
	if (tree == NULL)
		return 0;
	else
		return 1 + getNbArgCallFct(tree->middle);
}

int compareTypeArgs(VARSYM argFct, TREE argCallFct, FCTSYM symT, int nbArg)
{
	while (nbArg > 1) // les arguments des fonctions sont stockées au début de la liste chainée mais à l'envers
	{
		argFct = argFct->next;
		nbArg--;
	}   
              
	if (((strcmp(argFct->type, getType(argCallFct->left->left, symT))) == 0)) //l'argument de la fonction de la table des symboles a le même type que l'argument de l'appel de fonction
	{	
		if (argFct->prev != NULL) 
			return compareTypeArgs(argFct->prev, argCallFct->middle, symT, 0); // tester les arguments suivants
	}	
	else
	{
		return 0;
	}

        	return 1;
	
}

FCTSYM findFctOfCallFct(FCTSYM symT, char* name)
{
	if (symT == NULL)
	{
		printf("findFctOfCallFct: fct non trouvée"); // par récursivité si symT = NULL alors la fct associée à l'appel est pas trouvée dans la table des symboles
		return NULL;
	}
	
	FCTSYM tmp = symT;
	while (symT->next != NULL) { // rechercher la fonction dans les fonctions de même niveau que *symT*
		if (strcmp(symT->name,name) == 0)
			return symT;
		else
			symT = symT->next;
	}

	while (tmp->prev != NULL) { // rechercher la fonction dans les fonctions de même niveau que *symT*
		if (strcmp(tmp->name,name) == 0)
			return tmp;
		else
			tmp = tmp->prev;
	}


	if ((symT != NULL) && (strcmp(symT->name,name) == 0)) // on a trouver la fonction associée au callfct
	{
		return symT;
	}

	if ((tmp != NULL) && (strcmp(tmp->name,name) == 0)) // on a trouver la fonction associée au callfct 
	{
		return tmp;
	}
	
	return (FCTSYM) findFctOfCallFct(symT->childFct, name); // rechercher la fonction dans les fonctions fils de *symT*	
}



//tree = pointeur vers une fonction
int checkFct (TREE tree, FCTSYM symT, FCTSYM root)
{

  if (tree->type == AT_FCT)
    {
      if ((tree->other->left) != NULL)
	{
	  return checkFct (tree->other->left, symT, root); //Vérifier AT_ILISTCODE
	}
      else
	{
	  return 1;		//il n'y a rien dans le bloc Code
	}
    }
  if (tree->type == AT_ILISTCODE) // bloc qui vérifier une instruction de la fonction
	
    {
      if (tree->left != NULL)
	{
	  switch (tree->left->type)
	    {
	    case AT_AFFECTL:
	      {

		if (tree->left->middle->type == AT_CALLFCT) // Un call fonction qui se trouve dans une affectation
		{
			if ((checkCallFct(tree->left->middle, symT)) == 0)
			return 0;
		}
		
		if (tree->left->middle->type == AT_GET) // Un get qui se trouve dans une affectation 
		{
			if (!(strcmp (getType (tree->left->middle->left, symT), "matrix") == 0))
				printf(";ERROR_TYPE (GET : 1st arg)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->middle->left->line); 
	   		if (!(strcmp (getType (tree->left->middle->middle, symT), "int") == 0)) 
				printf(";ERROR_TYPE (GET : 2nd arg)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->middle->middle->line);
           		if (!(strcmp (getType (tree->left->middle->right, symT), "int") == 0))
				printf(";ERROR_TYPE (GET : 3rd arg)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->middle->right->line);
		}

	       	if (!(((strcmp(getType (tree->left->left, symT),getType (tree->left->middle, symT)) == 0) // il faut que les types de l'expression gauche et droit coincide et qu'il ne sont pas tous les deux de type "WRONG"
		      		&& (strcmp (getType (tree->left->left, symT), "WRONG") != 0)
		      		&& (strcmp (getType (tree->left->middle, symT), "WRONG")!= 0))))
		  {
			printf(";ERROR_TYPE (AFFECTL)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->line);
		    	return 0;
		  }

	      }
	      break;

	    case AT_OUT:
		if (tree->left->left->type == AT_GET) 
		{
			if (!(strcmp (getType (tree->left->left->left, symT), "matrix") == 0)) // le premier argument doit être de type "matrix"
				printf(";ERROR_TYPE (GET : 1st arg)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->left->left->line); 
	   		if (!(strcmp (getType (tree->left->left->middle, symT), "int") == 0))  // le deuxième argument doit être de type "int"
				printf(";ERROR_TYPE (GET : 2nd arg)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->left->middle->line);
           		if (!(strcmp (getType (tree->left->left->right, symT), "int") == 0))   // le troisième argument doit être de type "int"
				printf(";ERROR_TYPE (GET : 3rd arg)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->left->right->line);
		}
		if (!(strcmp(getType(tree->left->left,symT), "int")==0)) {  // le type de l'expression droite doit être de type "int"
			printf(";ERROR_TYPE (OUT)           NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->line);
			return 0;
		}
	      break;
	
	    case AT_IF:

	      //if (b) then vide else vide fi;
	      if ((tree->left->middle == NULL) && (tree->left->right == NULL) && (strcmp (getType (tree->left->left, symT), "bool") != 0) )
		{
			printf(";ERROR_TYPE (IF BLOC)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->line);
			return 0;
		}

	      //if (b) then nonVide else nonVide fi;
	      if (((tree->left->middle != NULL) && (tree->left->right != NULL))
		&&(!
		   (   (strcmp (getType (tree->left->left, symT), "bool") == 0)
		    && (checkFct (tree->left->middle, symT, root))
		    && (checkFct (tree->left->right, symT, root)))))
		{
		  	printf(";ERROR_TYPE (IF BLOC)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->line);
		  	return 0;
		}
	
		//if (b) then vide else nonVide fi;
	       if (((tree->left->middle == NULL) && (tree->left->right != NULL))
		&&(!
		   (   (strcmp (getType (tree->left->left, symT), "bool") == 0)
		    && (checkFct (tree->left->right, symT, root)))))
		{
		  	printf(";ERROR_TYPE (IF BLOC)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->line);
		  	return 0;
		}	


		//if (b) then nonVide else vide fi;
	      if (((tree->left->middle != NULL) && (tree->left->right == NULL))
		&&(!
		   ((strcmp (getType (tree->left->left, symT), "bool") == 0)
		    && (checkFct (tree->left->middle, symT, root)))))
		{
		  	printf(";ERROR_TYPE (IF BLOC)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->line);
		  	return 0;
		}
	      break;
	    case AT_WHILE:
		if (!(strcmp (getType (tree->left->left, symT), "bool") == 0)) // véfifier que le gardien est bien de type "bool"
		{
			printf(";ERROR_TYPE (WHILE BLOC)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->line);
			return 0;
		}
		if ((tree->left->middle != NULL) && (!(checkFct(tree->left->middle,symT, root)))) // si il y a des instructions dans le while, il faut tous les vérifier
		{
			printf(";ERROR_TYPE (WHILE BLOC)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->line);
			return 0;
		}
		break;
	    case AT_SWAP:
		if (!(   	   (tree->left->left->type == AT_ID) // on vérifie si c'est bien une variable
			        && (tree->left->middle->type == AT_ID) // on vérifie si c'est bien une variable
			        && ((strcmp(getType (tree->left->left, symT),getType (tree->left->middle, symT)) == 0) // vérifier si l'expression gauche 1 et l'expression gauche 2 ont le même type
		      		&& (strcmp (getType (tree->left->left, symT), "WRONG") != 0) // l'expression gauche 1 n'est pas de type "WRONG"
		      		&& (strcmp (getType (tree->left->middle, symT), "WRONG")!= 0)))) // l'expression gauche 2 n'est pas de type "WRONG"
		{
			printf(";ERROR_TYPE (SWAP)          NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->line);
			return 0;
		}

		break;
	    case AT_CALLFCT:	
		if ((checkCallFct(tree->left, symT)) == 0) // appel à la fonction *checkCallFct*
			return 0;
	    	break;

	    case AT_RETURN:
		if (strcmp(symT->retType,"nil") == 0) // si le type de retour de la fonction en cours est nil , cette dernière ne doit pas avoir de "return"
		{
			printf(";ERROR_TYPE (RETURN)                 Fonction declared with nil as return type can't have a return tokken , NumLineAtCode : %5d \n", tree->left->line);
			return 0;
		}
		else if (!((strcmp(getType(tree->left->left, symT), symT->retType )) == 0)) // vérifier que le type de retour de la fonction est égal au type de l'expression droite de "return"
		{

			printf("type : %s, type : %s ", getType(tree->left->left, symT), symT->retType);
			printf(";ERROR_TYPE (RETURN)                 Problem with the return type of the return tokken , NumLineAtCode : %5d \n", tree->left->line);
			return 0;
		}

		break;
	    case AT_CREATE:
	        if (!(strcmp (getType (tree->left->left, symT), "matrix") == 0)) { // vérifier que le premier argument est de type "matrix"
			printf(";ERROR_TYPE (CREATE : 1st arg)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->line);
		}

		else if ((!(strcmp(getType(tree->left->middle,symT), "int")==0))) { // vérifier que le deuxième argument est de type "int"
			printf(";ERROR_TYPE (CREATE : 2nd arg)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->middle->line);
		}

		else if ((!(strcmp(getType(tree->left->right,symT), "int")==0))) { // vérifier que le troisième argument est de type "int"
			printf(";ERROR_TYPE (CREATE : 3rd arg)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->middle->line);
		}

		break;
	    case AT_SET:
		if (!(strcmp (getType (tree->left->left, symT), "matrix") == 0)) { // vérifier que le premier argument est de type "matrix"
			printf(";ERROR_TYPE (SET : 1st arg)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->line);
		}
		
		else if (!(strcmp(getType(tree->left->middle,symT), "int")==0)) { // vérifier que le deuxième argument est de type "int"
			printf(";ERROR_TYPE (SET : 2nd arg)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->middle->line);
		}
		
		else if (!(strcmp(getType(tree->left->right,symT), "int")==0)) { // vérifier que le deuxième argument est de type "int"
			printf(";ERROR_TYPE (SET : 3rd arg)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->right->line);
		}
		
		else if (!(strcmp(getType(tree->left->other,symT), "int")==0)) { // vérifier que le deuxième argument est de type "int"
			printf(";ERROR_TYPE (SET : 4th arg)       NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->other->line);
		}
		
		break;

	    default:
	      		printf(";WARNING (NO VERIF TYPE)    NameFct : %20s , NumLineAtCode : %5d \n", symT->name ,tree->left->line);
	      	break;
	    }
	}
      
      if (tree->middle != NULL)
	{
	  return checkFct (tree->middle, symT,root); //vérifier les autres instructions de la fonction
	}

    }
  return 1;
}

int checkCallFct(TREE callFct, FCTSYM symT)
{
	if (findFctOfCallFct(symT, callFct->left->name) == NULL) //vérifier si le nom de la fonction existe dans les fonctions parentes de la table des symboles 
	{
	    	printf(";ERROR_TYPE (CALLFCT)       fonction not found : %20s, NumLineAtCode : %5d \n", callFct->left->name, callFct->line);
		return 0;
	}

	if (((findFctOfCallFct(symT, callFct->left->name))->nbArguments != getNbArgCallFct(callFct->middle)) ) //vérifier si le nombre d'arguments l'appel de la fonction est égal à celui de la fonction enregistrée dans la table des symbole
	{
				
	   	printf(";ERROR_TYPE (CALLFCT)       number of Args of fonction call  : %20s, NumLineAtCode : %5d \n", callFct->left->name, callFct->line);
		return 0;
	}
			
	if ((callFct->middle != NULL) && ((findFctOfCallFct(symT, callFct->left->name))->nextVar != NULL)) 
	{

		if ((compareTypeArgs( (findFctOfCallFct(symT, callFct->left->name)->nextVar), callFct->middle, symT,  getNbArgCallFct(callFct->middle))) == 0) //vérifier les arguments de l'appel de fonction avec les arguments de la fonction enregistrée dans la table des symboles
		{// idem AT_ADD
			printf(";ERROR_TYPE (CALLFCT)       Probleme d'arguments, NumLineAtCode : %5d \n", callFct->line);
			return 0;
		}
	}
	
	return 1;
				
}

char * getType (TREE tree, FCTSYM symT)
{
  VARSYM v;

  switch (tree->type)
    {
    case AT_ID:
	v = (VARSYM) searchVarAtFct (symT, tree->name); // rechercher dans la fonction stockée dans la table des symboles la variable
	if (v != NULL)
	  return v->type; // retourner le type de la variable
	else
	{
		FCTSYM temp;
		temp = symT;
		while(temp->parentFct!=NULL && v==NULL) // si on ne la trouve pas dans la fonction courante de la table des symboles alors on remonte pour voir si elle ne se trouve pas dans une fonction parente
		{
			temp = temp->parentFct;
			v = (VARSYM) searchVarAtFct(temp,tree->name);
			if(v!=NULL)
				return v->type;
		}
	  return "WRONG";	
	}
	break;
    case AT_NBPOS:
	return "int";
	break;
    case AT_NBNEG:
	return "int";
	break;
    case AT_BOOL:
	return "bool";
	break;
    case AT_ADD:
      if ((tree->left != NULL && tree->middle != NULL ) && ((strcmp (getType (tree->left, symT), "int") == 0) &&
	  (strcmp (getType (tree->middle, symT), "int") == 0))) // Si l'expression droite 1 et l'expression droite 2 doivent sont de type "int" renvoyer "int" sinon renvoyer "WRONG" 
	return "int";
      else
	return "WRONG";
	break;

    case AT_MIN:
      if ((tree->left != NULL && tree->middle != NULL ) && ((strcmp (getType (tree->left, symT), "int") == 0) &&
	  (strcmp (getType (tree->middle, symT), "int") == 0))) // idem AT_ADD
	return "int";
      else
	return "WRONG";
	break;

    case AT_MUL:
      if ((tree->left != NULL && tree->middle != NULL ) && ((strcmp (getType (tree->left, symT), "int") == 0) &&
	  (strcmp (getType (tree->middle, symT), "int") == 0))) // idem AT_ADD
	return "int";
      else
	return "WRONG";
	break;

    case AT_MOD:
      if ((tree->left != NULL && tree->middle != NULL ) && ((strcmp (getType (tree->left, symT), "int") == 0) &&
	  (strcmp (getType (tree->middle, symT), "int") == 0))) // idem AT_ADD
	return "int";
      else
	return "WRONG";
	break;

    case AT_DIV:
      if ((tree->left != NULL && tree->middle != NULL ) && ((strcmp (getType (tree->left, symT), "int") == 0) &&
	  (strcmp (getType (tree->middle, symT), "int") == 0))) // idem AT_ADD
	return "int";
      else
	return "WRONG";
	break;
    
    case AT_NOT:
      if ((tree->left != NULL) && ((strcmp (getType (tree->left, symT), "bool") == 0))) // Si l'expression droite de not est un bool renvoyer "bool" sinon renvoyer "WRONG"
	return "bool";
      else
	return "WRONG";
	break;

    case AT_AND:
      if ((tree->left != NULL && tree->middle != NULL ) && ((strcmp (getType (tree->left, symT), "bool") == 0) && 
	  (strcmp (getType (tree->middle, symT), "bool") == 0))) // Si l'expression droite 1 et l'expression droite 2 doivent sont de type "bool" renvoyer "bool" sinon renvoyer "WRONG" 
	return "bool";
      else
	return "WRONG";
	break;

    case AT_OR:
      if ((tree->left != NULL && tree->middle != NULL ) && ((strcmp (getType (tree->left, symT), "bool") == 0) &&
	  (strcmp (getType (tree->middle, symT), "bool") == 0))) // idem AT_AND
	return "bool"; 
      else
	return "WRONG";
	break;

    case AT_EQUAL:
      if ((tree->left != NULL && tree->middle != NULL ) && ((strcmp (getType (tree->left, symT), "int") == 0) &&
	  (strcmp (getType (tree->middle, symT), "int") == 0))) // idem AT_AND
	return "bool";
      else
	return "WRONG";
	break;
    case AT_SMALLER:
      if ((tree->left != NULL && tree->middle != NULL ) && ((strcmp (getType (tree->left, symT), "int") == 0) &&
	  (strcmp (getType (tree->middle, symT), "int") == 0))) // idem AT_AND
	return "bool";
      else
	return "WRONG";
	break;

    case AT_SMALLEQ:
      if ((tree->left != NULL && tree->middle != NULL ) && ((strcmp (getType (tree->left, symT), "int") == 0) &&
	  (strcmp (getType (tree->middle, symT), "int") == 0))) // idem AT_AND
	return "bool";
      else
	return "WRONG";
	break;

    case AT_GREATER:
      if ((tree->left != NULL && tree->middle != NULL ) && ((strcmp (getType (tree->left, symT), "int") == 0) &&
	  (strcmp (getType (tree->middle, symT), "int") == 0))) // idem AT_AND
	return "bool";
      else
	return "WRONG";
	break;

    case AT_GREATEQ:
      if ((tree->left != NULL && tree->middle != NULL ) && ((strcmp (getType (tree->left, symT), "int") == 0) &&
	  (strcmp (getType (tree->middle, symT), "int") == 0))) // idem AT_AND
	return "bool";
      else
        return "WRONG";
	break;

    case AT_IN:
	return "int"; // renvoyer le type "int"
	break;
   
    case AT_CALLFCT:
	if (findFctOfCallFct(symT, tree->left->name) != NULL) { 
		return (findFctOfCallFct(symT, tree->left->name))->retType; // retourner le type de la fonction à partir de la table des symboles
	}
	else 
     		return "WRONG";
	break;
    case AT_GET:
        if (((strcmp (getType (tree->left, symT), "matrix") == 0)) // si le premier argument est de type matrix && deuxième de type "int" && troisième de type "int" alors renvoyer "int" sinon renvoyer "WRONG"
	   && ((strcmp (getType (tree->middle, symT), "int") == 0)) 
           && ((strcmp (getType (tree->right, symT), "int") == 0))) {
		return "int";
	}
	else 
		return "WRONG";
	break;
    }
}
